/*
 * ENUM Discoverer. ENUM Demonstration software, demonstrating the potential of ENUM.
 * 
 * Copyright (C) 2009 SIDN and ISOC.nl
 * 
 * This file is part of ENUM Discoverer.
 *
 * ENUM Discoverer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ENUM Discoverer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with ENUM Discoverer.  If not, see <http://www.gnu.org/licenses/>.
 */

package nl.sidn.enumdiscoverer.callhandling;

import java.util.ArrayList;

import nl.sidn.enumdiscoverer.AppConstants;
import nl.sidn.enumdiscoverer.EditPreferences;
import nl.sidn.enumdiscoverer.EnumClient;
import nl.sidn.enumdiscoverer.EnumRecord;
import nl.sidn.enumdiscoverer.LogConfig;
import nl.sidn.enumdiscoverer.R;
import nl.sidn.enumdiscoverer.ui.incall.EnumInCallScreen;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.util.Log;

/**
 * Background task for doing initial ENUM lookup in response to an incoming/outgoing phone call.
 * 
 * Does an ENUM lookup. If it finds any ENUM records, it posts a notification to the
 * notification bar. If the user clicks the notification, then the ENUM Discoverer
 * UI will be displayed.
 * 
 * @author	Grant Patterson (grant.patterson@catalyst.net.nz)
 */
public class CallHandlingService extends Service {

	static final String TAG = LogConfig.getLogTag(CallHandlingService.class);
	// whether DEBUG level logging is enabled (whether globally, or explicitly for this log tag)
	static final boolean DEBUG = LogConfig.isDebug(TAG);
	// whether VERBOSE level logging is enabled
	static final boolean VERBOSE = LogConfig.VERBOSE;
	
	public static final String DIRECTION = AppConstants.PACKAGE_NAME + ".extra.DIRECTION";
	public static final String DIRECTION_INCOMING = "incoming";
	public static final String DIRECTION_OUTGOING = "outgoing";
	
	
	@Override
	public void onStart(Intent intent, int startId) {
		super.onStart(intent, startId);
		
		if (VERBOSE) Log.v(TAG, "CallHandlingService started for intent " + intent);
		
		String number = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
		if (number == null) {
			Log.e(TAG, "No " + Intent.EXTRA_PHONE_NUMBER + " extra in intent " + intent);
			return;
		}
		
		String direction = intent.getStringExtra(DIRECTION);
		if (!isValidDirection(direction)) {
			Log.e(TAG, "Invalid " + DIRECTION + " (" + direction + ") in intent " + intent);
			return;
		}
		
		// perform the lookup on another thread
		new EnumLookupTask(number).execute();
	}
	
	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}
	
	private boolean isValidDirection(String direction) {
		
		return DIRECTION_INCOMING.equals(direction) || DIRECTION_OUTGOING.equals(direction);
	}
	
	/**
	 * Performs an ENUM lookup for the phone number
	 * @param number The phone number to perform the lookup for
	 * @return The ENUM records resulting from the lookup
	 */
	ArrayList<EnumRecord> doEnumLookup(String number) {
		
		if (DEBUG) Log.d(TAG, "Doing ENUM lookup of " + number);
		
		EnumClient enumClient = createEnumClient();
		ArrayList<EnumRecord> results = enumClient.doEnumLookup(number);
		
		if (results.isEmpty()) {
			Log.i(TAG, "ENUM lookup failed to find any details about " + number);
		} else {
			Log.i(TAG, "ENUM lookup found details for " + number);
		}
		
		return results;
	}
	
	/**
	 * Creates a new EnumClient, initialised with values from the applications shared preferences
	 * @return a new EnumClient
	 */
	private EnumClient createEnumClient() {
		
		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
		
		String suffix = prefs.getString(getString(R.string.pref_dns_suffix_key), null);
		String[] servers = EditPreferences.getCustomDnsServers(prefs, this);
		String countryCode = prefs.getString(getString(R.string.pref_country_code_key), null);
		
		return new EnumClient(suffix, servers, countryCode);
	}
	
	/**
	 * Invoked when background ENUM lookup task has finished
	 * @param number The phone number the lookup was performed for
	 * @param enumData The results of the ENUM lookup
	 */
	void onEnumLookupDone(String number, ArrayList<EnumRecord> enumData) {
		
		if (enumData.isEmpty()) {
			// nothing found. Don't show a notification
			return;
		}
		
		if (VERBOSE) Log.v(TAG, "Creating in-call screen notification for call " + number);
		showNotification(number, enumData);
	}
	
	/**
	 * Show a notification in the notification bar
	 * @param number
	 * @param enumData
	 */
	private void showNotification(String number, ArrayList<EnumRecord> enumData) {
		
		Intent intent = new Intent(this, EnumInCallScreen.class);
		intent.setData(Uri.fromParts("tel", number, null));
		intent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
		intent.putParcelableArrayListExtra(EnumInCallScreen.EXTRA_ENUM_RECORDS, enumData);
		// we need the MULTIPLE_TASK flag to prevent an existing EnumInCallScreen instance being
		// restarted with its previous intent.
		intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
		
		int piFlags = PendingIntent.FLAG_ONE_SHOT;
		PendingIntent pi = PendingIntent.getActivity(this, 0, intent, piFlags);
		
		Notification ntfn = new Notification();
		ntfn.icon = R.drawable.enum_notification;
		ntfn.when = System.currentTimeMillis();
		ntfn.flags = Notification.FLAG_AUTO_CANCEL;
		ntfn.setLatestEventInfo(this, getString(R.string.app_name),
				getString(R.string.call_notification_text, number), pi);
		
		NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
		nm.notify(R.id.call_notification, ntfn);
	}
	
	
	/**
	 * Services run on the process's main thread (along with the UI). To avoid impacting
	 * anything else sharing the main thread, we do the ENUM lookup on a different thread
	 * using an AsyncTask.
	 */
	private class EnumLookupTask extends AsyncTask<Void, Void, ArrayList<EnumRecord>> {
		
		// safely accessible from main and background threads
		private final String phoneNumber;
		
		EnumLookupTask(String phoneNumber) {
			
			this.phoneNumber = phoneNumber;
		}
		
		@Override
		protected ArrayList<EnumRecord> doInBackground(Void... params) {
			
			return doEnumLookup(phoneNumber);
		}
		
		@Override
		protected void onPostExecute(ArrayList<EnumRecord> enumData) {
			
			onEnumLookupDone(phoneNumber, enumData);
		}
	}
}
